package main

import (
	"bufio"
	"context"
	"flag"
	"log"

	"gitee.com/blinkspark/barry/utils"

	"github.com/libp2p/go-libp2p"
	pubsub "github.com/libp2p/go-libp2p-pubsub"
	"github.com/libp2p/go-libp2p/core/crypto"
	"github.com/libp2p/go-libp2p/core/network"
)

var (
	keyPath      = flag.String("k", "priv.key", "private key path")
	port         = flag.Int("p", 0, "port")
	genKey       = flag.Bool("g", false, "generate key")
	holePunching = flag.Bool("h", false, "enable hole punching")
)

func init() {
	flag.Parse()
}

func main() {
	var (
		priv crypto.PrivKey
		err  error
	)

	if *genKey {
		priv, _, err = utils.GenAndSaveKeyPair(*keyPath)
		if err != nil {
			log.Panic(err)
		}
	}

	if priv == nil {
		priv, err = utils.LoadKey(*keyPath)
		if err != nil {
			log.Panic(err)
		}
	}

	opts := libp2p.ChainOptions(libp2p.Identity(priv), libp2p.ListenAddrStrings(utils.GetListenAddrStrings(*port)...))
	if *holePunching {
		opts = libp2p.ChainOptions(opts, libp2p.EnableHolePunching())
	}

	// Create a new libp2p host
	host, err := libp2p.New(opts)
	if err != nil {
		log.Panic(err)
	}
	ctx := context.Background()
	psNode, err := pubsub.NewGossipSub(ctx, host)
	if err != nil {
		log.Panic(err)
	}
	topic, err := psNode.Join("gitee.com/blinkspark/barry")
	if err != nil {
		log.Panic(err)
	}
	sub, err := topic.Subscribe()
	if err != nil {
		log.Panic(err)
	}
	defer sub.Cancel()
	go func() {
		for {
			msg, err := sub.Next(ctx)
			if err != nil {
				log.Panic(err)
			}
			log.Printf("Received message: %s\n", string(msg.Data))
		}
	}()

	host.SetStreamHandler("/gitee.com/blinkspark/barry/0.1.0", echoHandler)

	// Print host info
	log.Println("Host Addrs:")
	for _, addr := range host.Addrs() {
		log.Printf("%s/p2p/%s\n", addr, host.ID())
	}
	//run forever
	select {}
}

func echoHandler(stream network.Stream) {
	log.Println("Got a new stream!")
	defer stream.Close()
	// Create a buffer stream for non blocking read and write.
	rw := bufio.NewReadWriter(bufio.NewReader(stream), bufio.NewWriter(stream))
	for {
		msg, err := rw.ReadString('\n')
		if err != nil {
			log.Println("Error reading stream: ", err)
			break
		}
		log.Println("Received message:" + msg)
		if msg == "_EXIT_\n" {
			break
		}
		// Echo all received messages.
		_, err = rw.WriteString("Message received: " + msg + "\n")
		if err != nil {
			log.Println("Error writing response: ", err)
			break
		}
		rw.Flush()
	}
	log.Println("Closing stream...")
}
